package cn.edu.cuit.elena.web.service;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.Map.Entry;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import cn.edu.cuit.elena.db.DataSource;
import cn.edu.cuit.elena.db.Schema;
import cn.edu.cuit.elena.db.Schema.Table;
import cn.edu.cuit.elena.trace.Tracer;
import cn.edu.cuit.elena.transaction.controller.IdentifiedRunnable;
import cn.edu.cuit.elena.transaction.query.HiveQueryExecutor;
import cn.edu.cuit.elena.transaction.query.HiveQueryExecutor.HiveIdentifiableRunnableImp;
import cn.edu.cuit.elena.transaction.system.UserContext;
import cn.edu.cuit.elena.web.System;
import cn.edu.cuit.elena.web.service.ServiceServlet.ServiceConstants;
import cn.edu.cuit.elena.web.tool.JsonHelper;

public class CommonQueryServlet
    extends HttpServlet
{
    static final String TABLES = "tables";
    static final String COMMON_QUERY = "commonquery";
    String CONSOLE = "console";
    String RESULT = "result";

    String jsonCache_1 = "";
    Map<String, String> resultCache = new HashMap<String, String>();

    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    /**
     * The doGet method of the servlet. <br>
     * 
     * This method is called when a form has its tag value method equals to get.
     * 
     * @param request
     *            the request send by the client to the server
     * @param response
     *            the response send by the server to the client
     * @throws ServletException
     *             if an error occurred
     * @throws IOException
     *             if an error occurred
     */
    public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException
    {
        doPost( request, response );
    }

    /**
     * The doPost method of the servlet. <br>
     * 
     * This method is called when a form has its tag value method equals to post.
     * 
     * @param request
     *            the request send by the client to the server
     * @param response
     *            the response send by the server to the client
     * @throws ServletException
     *             if an error occurred
     * @throws IOException
     *             if an error occurred
     */
    public void doPost( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException
    {

        response.setContentType( "text/html" );
        PrintWriter out = response.getWriter();
        String para = request.getParameter( "kind" );
        UserContext userContext = (UserContext) request.getSession().getAttribute( ServiceConstants.USERCONTEXT.mark() );
        Map<String, List<Map<String, String>>> cache = DataMigrationServlet.cache;

        String username = request.getParameter( "username" );
        String password = request.getParameter( "password" );
        String url = request.getParameter( "url" );
        String driver = request.getParameter( "driver" );

        if( TABLES.equals( para ) )
        {
            List<Map<String, String>> cacheList = cache.get( userContext.getIdentifier() );
            if( cacheList == null )
            {
                cache.put( userContext.getIdentifier(), new LinkedList<Map<String, String>>() );
            }

            String json = JsonHelper.getNewInstance().toJson( cacheList );

            Tracer.debugTrace( DataMigrationServlet.class, "migrating Tables json : " + json, null );
            out.write( json );
            out.close();
            out.flush();
        }
        else if( COMMON_QUERY.equals( para ) )
        {

            List<Map<String, String>> cacheList = cache.get( userContext.getIdentifier() );

            String chooseTable = request.getParameter( "choosetable" ) == null ? "" : request
                .getParameter( "choosetable" );
            String chooseField = request.getParameter( "choosefield" ) == null ? "" : request
                .getParameter( "choosefield" );
            String query_pattern = request.getParameter( "query_pattern" ) == null ? "" : request
                .getParameter( "query_pattern" );
            String jointable = request.getParameter( "jointable" ) == null ? "" : request.getParameter( "jointable" );
            String join_pattern = request.getParameter( "join_pattern" ) == null ? "" : request
                .getParameter( "join_pattern" );
            //            String joinfield = request.getParameter( "joinfield" );
            String condition = request.getParameter( "condition" ) == null ? "" : request.getParameter( "condition" );
            String selectCondition = request.getParameter( "selectcondition" ) == null ? "" : request
                .getParameter( "selectcondition" );

            //choose a query_pattern
            if( query_pattern == null )
            {
                out.write( "5" );
                out.flush();
                out.close();
                return;
            }

            HiveQueryExecutor hiveQueryExecutor = System.getHiveQueryExecutor();

            //Get datasource in BD
            DataSource dataSource = null;
            for( DataSource dataSource0 : userContext.getDataSourceManager().getDataSources() )
            {
                if( dataSource0.toString().equals( url + username + password + driver ) )
                {
                    dataSource = dataSource0;
                }
            }
            Schema schema = null;

            if( dataSource == null )
            {
                out.write( "9" );
                out.flush();
                out.close();
                return;
            }
            try
            {
                schema = dataSource.loadSchema( userContext.getIdentifier() );
            }
            catch( SQLException e )
            {
                Tracer.fatalTrace( CommonQueryServlet.class, "Can't load schema due to" + e.getMessage(), e );
                out.close();
                return;
            }
            Table table = schema.getTables().get( chooseTable );

            if( table == null )
            {
                out.write( "10" );
                out.flush();
                out.close();
                return;
            }

            /**
             * Task ID = chooseTable+chooseField+query_pattern+jointable+join_pattern+condition+
             * selectCondition. If anyone is <code>null</code>, it should be written to "".
             * */
            String taskId = chooseTable + chooseField + query_pattern + jointable + join_pattern + condition
                + selectCondition;

            //single table query
            if( query_pattern.equals( "1" ) )
            {
                String hiveTableName = table.getHiveTableName();

                /**
                 * Construct query statement.!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                 * */
                //                Interpreter interpreter = new Interpreter(false,chooseTable,chooseField,condition);
                //                hiveQueryExecutor.doQuery(interpreter);

                //No condition
                if( selectCondition == null || selectCondition.equals( "" ) )
                {
                    //
                    //
                    //Or use nanoTime as task id
                    //
                    //
                    hiveQueryExecutor.doQuery( taskId, "select * from " + hiveTableName );
                }
                else
                {
                    hiveQueryExecutor.doQuery( taskId, "select * from " + hiveTableName + " where " + chooseField + " "
                        + selectCondition );
                }
            }
            //multiple table query
            else if( query_pattern.equals( "2" ) )
            {
                String hiveTableName = table.getHiveTableName();
                Table choosenTable = schema.getTables().get( jointable );
                if( choosenTable == null )
                {
                    out.write( "11" );
                    out.flush();
                    out.close();
                    return;
                }

                String choosenHiveTableName = choosenTable.getHiveTableName();

                //choose a join pattern
                if( join_pattern == null )
                {
                    out.write( "6" );
                    out.flush();
                    out.close();
                    return;
                }
                //['No join','1'],
                if( join_pattern.equals( "1" ) )
                {
                    if( selectCondition == null || selectCondition.equals( "" ) )
                    {

                        hiveQueryExecutor.doQuery( taskId, "select * from " + hiveTableName );
                    }
                    else
                    {

                        hiveQueryExecutor.doQuery( taskId, "select * from " + hiveTableName + ","
                            + choosenHiveTableName + " where " + selectCondition );
                    }
                }
                //  ['Left outter join','2'],
                else if( join_pattern.equals( "2" ) )
                {
                    if( selectCondition == null || selectCondition.equals( "" ) )
                    {
                        selectCondition = "";
                    }
                    else
                    {
                        selectCondition = " where " + selectCondition;
                    }
                    hiveQueryExecutor.doQuery( taskId, "select * from " + hiveTableName + " LEFT OUTER JOIN "
                        + choosenHiveTableName + choosenHiveTableName + " on " + condition + selectCondition );
                }// ['Right outter join','3'],
                else if( join_pattern.equals( "3" ) )
                {
                    if( selectCondition == null || selectCondition.equals( "" ) )
                    {
                        selectCondition = "";
                    }
                    else
                    {
                        selectCondition = " where " + selectCondition;
                    }
                    hiveQueryExecutor.doQuery( taskId, "select * from " + hiveTableName + " RIGHT OUTER JOIN "
                        + choosenHiveTableName + choosenHiveTableName + " on " + condition + selectCondition );
                }
                // ['Just join','4']
                else if( join_pattern.equals( "4" ) )
                {

                    if( selectCondition == null || selectCondition.equals( "" ) )
                    {
                        selectCondition = "";
                    }
                    else
                    {
                        selectCondition = " where " + selectCondition;
                    }
                    hiveQueryExecutor.doQuery( taskId, "select * from " + hiveTableName + " join "
                        + choosenHiveTableName + choosenHiveTableName + " on " + condition + selectCondition );
                }
                else
                {
                    //choose a join pattern
                    out.write( "7" );
                    out.flush();
                    out.close();
                    return;
                }
            }
            else
            {
                //choose a query pattern
                out.write( "8" );
                out.flush();
                out.close();
                return;
            }
        }
        else if( CONSOLE.equals( para ) )
        {
            Map<String, IdentifiedRunnable> tasks = System.getSystemTaskMonitor().getTasks();
            Map<String, IdentifiedRunnable> retiredTasks = System.getSystemTaskMonitor().getRetiredTasks();
            List<Map<String, String>> list = new LinkedList<Map<String, String>>();
            for( Entry<String, IdentifiedRunnable> entry : tasks.entrySet() )
            {
                String taskId = entry.getKey();
                IdentifiedRunnable task = entry.getValue();
                if( task instanceof HiveIdentifiableRunnableImp )
                {
                    Map<String, String> temp = new HashMap<String, String>();
                    temp.put( "submitetime", ( (HiveIdentifiableRunnableImp) task ).getSubmitTime() );
                    temp.put( "hql", ( (HiveIdentifiableRunnableImp) task ).getHQL() );
                    temp.put( "hidden", ( (HiveIdentifiableRunnableImp) task ).getIdentifier() );
                    temp.put( "progress", String.valueOf( task.getProgression() ) );
                    list.add( temp );
                }
            }

            for( Entry<String, IdentifiedRunnable> entry : retiredTasks.entrySet() )
            {
                String taskId = entry.getKey();
                IdentifiedRunnable task = entry.getValue();
                if( task instanceof HiveIdentifiableRunnableImp )
                {
                    Map<String, String> temp = new HashMap<String, String>();
                    temp.put( "submitetime", ( (HiveIdentifiableRunnableImp) task ).getSubmitTime() );
                    temp.put( "hql", ( (HiveIdentifiableRunnableImp) task ).getHQL() );
                    temp.put( "hidden", ( (HiveIdentifiableRunnableImp) task ).getIdentifier() );
                    temp.put( "progress", String.valueOf( task.getProgression() ) );
                    list.add( temp );
                }
            }

            String json = JsonHelper.getNewInstance().toJson( list );
            if( jsonCache_1.equals( json ) || json.equals( "[]" ) )
            {
                out.close();
            }
            else
            {
                jsonCache_1 = json;
                Tracer.debugTrace( CommonQueryServlet.class, "console json : " + json, null );
                out.write( json );
                out.flush();
                out.close();
            }
        }
        else if( RESULT.equals( para ) )
        {
            String taskId = request.getParameter( "taskId" );
            IdentifiedRunnable task = System.getSystemTaskMonitor().getTaskById( taskId ) == null ? System
                .getSystemTaskMonitor().getRetiredTaskById( taskId ) : System.getSystemTaskMonitor().getTaskById(
                taskId );
            if( task == null )
            {

                out.close();
                return;
            }
            ResultSet resultSet = (ResultSet) task.getResult().getInfomation();

            StringBuilder stringBuilder = new StringBuilder();

            if( resultSet != null )
            {
                try
                {
                    ResultSetMetaData metaData = resultSet.getMetaData();
                    int columnCount = metaData.getColumnCount();
                    //                    stringBuilder.append( " Result:<BR/><table border='0.5'><tr>" );
                    for( int index = 1; index <= columnCount; index++ )
                    {
                        //                        stringBuilder.append( "<td>" );
                        stringBuilder.append( "     " );
                        stringBuilder.append( metaData.getColumnName( index ) );
                        //                        stringBuilder.append( "</td>" );
                        stringBuilder.append( "     " );
                    }
                    //                    stringBuilder.append( "</tr>" );
                    stringBuilder.append( "\n" );

                    //                    if( resultCache.get( taskId ) != null &&resultCache.get( taskId ).equals( new String(stringBuilder) ) )
                    if( resultCache.get( taskId ) != null )
                    {
                        out.write( resultCache.get( taskId ) );
                        out.flush();
                        out.close();
                        return;
                    }
                    while( resultSet.next() )
                    {
                        //                        stringBuilder.append( "<tr>" );
                        for( int index = 1; index <= columnCount; index++ )
                        {
                            //                            stringBuilder.append( "<td>" );
                            stringBuilder.append( "     " );
                            stringBuilder.append( resultSet.getString( index ) );
                            //                            stringBuilder.append( "</td>" );
                            stringBuilder.append( "     " );
                        }
                        //                        stringBuilder.append( "</tr>" );
                        stringBuilder.append( "\n" );
                    }
                    //                    stringBuilder.append( "</table> " );

                    String outputString = new String( stringBuilder );
                    resultCache.put( taskId, outputString );
                    Tracer.debugTrace( CommonQueryServlet.class, "query result : " + outputString, null );
                    out.write( outputString );
                    out.flush();
                    out.close();

                }
                catch( Exception e )
                {
                    Tracer.debugTrace( CommonQueryServlet.class, "Result Exception : " + e.getMessage(), null );
                    out.write( "" );
                    out.flush();
                    out.close();
                }
            }
        }
        else if( "refresh".equals( para ) )
        {
            jsonCache_1 = "refress";
        }
        else if( "exportresult".equalsIgnoreCase( para ) )
        {
            String dir = request.getParameter( "dir" );
            String content = request.getParameter( "resultset" );
            try
            {
                FileWriter fileWriter = new FileWriter( new File( dir ) );
                fileWriter.write( content );
                fileWriter.flush();
                fileWriter.close();
                out.write( "1" );
                out.close();
            }
            catch( Exception e )
            {
                Tracer.fatalTrace( CommonQueryServlet.class, "Export result Exception : " + e.getMessage(), e );
            }
        }
    }
}
